package com.xiaobias;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;

public class PollerIO extends ServerThread {

    private final Server server;

    private final Queue<SocketChannel> acceptedChannels;

    public PollerIO(String name, Server server) {
        super(name);
        this.server = server;
        this.acceptedChannels = new LinkedBlockingDeque<>();
    }

    public void addAcceptedConnection(SocketChannel socketChannel) {
        this.acceptedChannels.add(socketChannel);
        wakeupSelector();
    }

    @Override
    public void run() {
        while(!server.stopped) {
            doSelect();
            doAcceptedConnections();
        }
        closeSelector();
    }

    private void doAcceptedConnections() {
        SocketChannel socketChannel = null;
        while (!server.stopped &&(socketChannel = this.acceptedChannels.poll()) != null) {
            try {
                socketChannel.register(this.selector, SelectionKey.OP_READ);
            } catch (ClosedChannelException e) {
                e.printStackTrace();
            }
        }
    }

    private void doSelect() {
        try {
            this.selector.select();
            Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (!key.isValid()) {
                    continue;
                }
                if (key.isReadable() || key.isWritable()) {
                    handlerIO(key);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handlerIO(SelectionKey key) {
         if (key.isReadable()) {
             SocketChannel socketChannel = (SocketChannel) key.channel();
             ByteBuffer readBuffer = ByteBuffer.allocate(256);
             try {
                 int read = socketChannel.read(readBuffer);
                 if (read > 0) {
                     // 将数据封装成一个任务扔给线程池做业务
                     BuTask buTask = new BuTask(readBuffer, socketChannel);
                     this.server.addBusTask(buTask);
                 }
             } catch (IOException e) {
                 try {
                     socketChannel.close();
                 } catch (IOException ex) {
                     ex.printStackTrace();
                 }
             }
         }
    }
}
